/* $Id$ */
/* vim: set filetype=verilog ts=8 sw=4 tw=132: */
/*****************************************************************************
 
              (c) Copyright 1987 - 2012,  VIA Technologies, Inc.       
                            ALL RIGHTS RESERVED                            
                                                                     
 This design and all of its related documentation constitutes valuable and
 confidential property of VIA Technologies, Inc.  No part of it may be
 reproduced in any form or by any means   used to make any transformation
 / adaptation / redistribution without the prior written permission from the
 copyright holders. 
 
------------------------------------------------------------------------------

  DESCRIPTION:

  FEATURES:

  TODO:

  AUTHORS:
     Shawn Fang
    
------------------------------------------------------------------------------
                             REVISION HISTORY
    $Log$

*****************************************************************************/
module fetch(
	/*
	output [31:0] pc,
	output [3:0] icode,
	output [3:0] ifun,
	output [3:0] rA,
	output [3:0] rB,
	output [31:0] valC,
	output reg [31:0] valP,
	output instrErr,

	input  clock,
	input  reset,
	input  [47:0] instr,
	input  [31:0] valM,
	input  i_ok,
	input  cnd*/
	//outputs
       output  [31:0] f_pc,
       output  [3:0] f_icode,
       output [3:0] f_ifun,
       output [3:0] f_rA,
       output [3:0] f_rB,
       output [31:0] f_valC,
       output reg[31:0] f_valP,
       output reg[2:0] f_stat,

       //inputs
       input  clock,
	input  reset,
	input  i_ok,
	input  [47:0] instr,
	input  [3:0] w_icode,
	input  [3:0] m_icode,
	input  [31:0] m_valA,
	input  [31:0] w_valM,
	input  m_cnd,
	input  F_stall,
	input  F_bubble,

	//add by shawn
	input  [2:0] m_stat
   );

wire   need_regids;
wire   need_valC;
wire   instr_valid;
wire   [31:0] predPC_i,predPC_o;

assign f_icode=i_ok ? instr[7:4] : `INOP;
assign f_ifun=i_ok ? instr[3:0] : 4'h0;
assign f_rA=need_regids?instr[15:12]:4'h0;
assign f_rB=need_regids?instr[11:8]:4'h0;
assign f_valC=need_regids?instr[47:16]:instr[39:8];

always @ (need_regids or need_valC or f_pc)
begin
    case({need_regids,need_valC})
    2'b00:f_valP<=f_pc+1;
    2'b01:f_valP<=f_pc+5;
    2'b10:f_valP<=f_pc+2;
    2'b11:f_valP<=f_pc+6;
    default:f_valP<=f_pc;
    endcase
end
assign need_regids=(f_icode==`IRRMOVL
		||f_icode==`IOPL
		||f_icode==`IPUSHL
		||f_icode==`IPOPL
		||f_icode==`IIRMOVL
		||f_icode==`IRMMOVL
		||f_icode==`IMRMOVL)?1'b1:1'b0;
assign instr_valid=(f_icode==`INOP
		||f_icode==`IHALT
		||(f_icode==`IRRMOVL && f_ifun<=4'h6)
		||f_icode==`IIRMOVL
		||f_icode==`IRMMOVL
		||f_icode==`IMRMOVL
		||(f_icode==`IOPL && f_ifun<=4'h3)
		||(f_icode==`IJXX && f_ifun<=4'h6)
		||f_icode==`ICALL
		||f_icode==`IRET
		||f_icode==`IPUSHL
		||f_icode==`IPOPL)?1'b1:1'b0;
assign instrErr=~instr_valid || ~i_ok;
assign need_valC=(f_icode==`IIRMOVL
		||f_icode==`IRMMOVL
		||f_icode==`IMRMOVL
		||f_icode==`IJXX
		||f_icode==`ICALL)?1'b1:1'b0;
always @ (F_stall or i_ok or instrErr or f_icode or f_stat or reset)
    begin
	if(reset)
	    f_stat<=`SAOK;
	else
	begin
	    if(F_stall)
		f_stat<=f_stat;
	    else if(~i_ok)
		f_stat<=`SADR;
	    else if(instrErr)
		f_stat<=`SINS;
	    //else if(f_icode==`IHALT)
	//	f_stat<=`SHLT;
	    else
		f_stat<=`SAOK;
	end
    end
reg    [31:0] d_pc,e_pc,m_pc;
  always @ (posedge clock or posedge reset)
    begin
	if(reset)
	begin
	    d_pc<=0;
	    e_pc<=0;
	    m_pc<=0;
	end
	else
	begin
	    d_pc<=f_pc;
	    e_pc<=d_pc;
	    m_pc<=e_pc;
	end
    end

 select_PC U_select_PC
    (
	.M_valA(m_valA),
	.W_valM(w_valM),
	.predPC(predPC_o),
	.M_icode(m_icode),
	.W_icode(w_icode),
	.f_icode(f_icode),
	.M_cnd(m_cnd),
	.f_stat(f_stat),
	.m_stat(m_stat),
	.m_pc(m_pc),
	.f_pc(f_pc)
    );

predict_pc U_predict_pc
    (
	.clock(clock),
	.reset(reset),
	.f_valC(f_valC),
	.f_valP(f_valP),
	.f_icode(f_icode),
	.predPC(predPC_i));

 ppregs_F U_ppregs_F
    (
	.clock(clock),
	.reset(reset),
	.F_stall(F_stall),
	.predPC_i(predPC_i),
	.predPC_o(predPC_o)
    );
    
endmodule

